/**
 * Copyright (c) 2018, 西安星沙网络科技-版权所有
 *
 * Licensed under the GNU Lesser General Public License (LGPL) ,Version 3.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.gnu.org/licenses/lgpl-3.0.txt
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.waleychain.exchange.service.impl.trade;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.github.pagehelper.Page;

import cn.waleychain.exchange.core.Global;
import cn.waleychain.exchange.core.SysParaKey;
import cn.waleychain.exchange.core.cache.CacheConts;
import cn.waleychain.exchange.core.constant.DDIC;
import cn.waleychain.exchange.core.constant.DDIC.TradeType;
import cn.waleychain.exchange.core.constant.OperatePaymentType;
import cn.waleychain.exchange.core.entity.PageInfo;
import cn.waleychain.exchange.core.exec.ClientException;
import cn.waleychain.exchange.core.exec.CoinWalletBalanceNotEnoughException;
import cn.waleychain.exchange.core.exec.CoinWalletFreezeNotEnoughException;
import cn.waleychain.exchange.core.exec.CoinWalletOperationException;
import cn.waleychain.exchange.core.exec.CoinWalletStatusFreezeException;
import cn.waleychain.exchange.core.exec.CoinWalletSyncException;
import cn.waleychain.exchange.core.exec.TradeOrderMatchFailedException;
import cn.waleychain.exchange.core.exec.TradeOrderMatchRepeatException;
import cn.waleychain.exchange.core.logger.LoggerHelper;
import cn.waleychain.exchange.core.result.RetResultCode;
import cn.waleychain.exchange.core.utils.BigDecimalUtils;
import cn.waleychain.exchange.core.utils.PageHelper;
import cn.waleychain.exchange.core.utils.SequenceUtils;
import cn.waleychain.exchange.core.utils.StringUtils;
import cn.waleychain.exchange.core.vaildate.VaildateHelper;
import cn.waleychain.exchange.dao.CoinInfoMapper;
import cn.waleychain.exchange.dao.DealOrderMapper;
import cn.waleychain.exchange.dao.EntrustOrderMapper;
import cn.waleychain.exchange.dao.MarketMapper;
import cn.waleychain.exchange.feign.CoinWalletServiceFeign;
import cn.waleychain.exchange.feign.ConfigServiceFeign;
import cn.waleychain.exchange.feign.UserServiceFeign;
import cn.waleychain.exchange.model.CoinInfo;
import cn.waleychain.exchange.model.CoinWallet;
import cn.waleychain.exchange.model.DealOrder;
import cn.waleychain.exchange.model.EntrustOrder;
import cn.waleychain.exchange.model.Market;
import cn.waleychain.exchange.model.UserInfo;
import cn.waleychain.exchange.service.impl.BaseServiceImpl;
import cn.waleychain.exchange.service.trade.CacheDataService;
import cn.waleychain.exchange.service.trade.TradeService;

@Service
public class TradeServiceImpl extends BaseServiceImpl implements TradeService {

	private static final Logger mLog = LoggerFactory.getLogger(TradeServiceImpl.class);
	
	@Autowired
	private DealOrderMapper dealOrderMapper;
	
	@Autowired
	private EntrustOrderMapper entrustOrderMapper;
	
	@Autowired
	private UserServiceFeign userFeign;
	
	@Autowired
	private MarketMapper marketMapper;
	
	@Autowired
	private CoinInfoMapper coinMapper;
	
	@Autowired
	private ConfigServiceFeign configFeign;
	
	@Autowired
	private CoinWalletServiceFeign coinWalletFeign;
	
	@Autowired
	private CacheDataService cacheDataService;
	
	@Override
	public PageInfo<DealOrder> fetchDealOrderPageList(Long marketId, String userName, Long dealId, Integer status, Integer type, Integer showCount, Integer currentPage) throws Exception {

		PageHelper.startPage(currentPage, showCount);
		Page<DealOrder> page = dealOrderMapper.fetchDealOrderPageList(marketId, userName, dealId, status, type);
		
		return new PageInfo<>(page);
	}

	@Override
	public PageInfo<EntrustOrder> fetchEntrustOrderPageList(Long marketId, Long userId, Integer type, Integer status, Integer showCount, Integer currentPage) throws Exception {

		PageHelper.startPage(currentPage, showCount);
		Page<EntrustOrder> page = entrustOrderMapper.fetchEntrustOrderPageList(marketId, userId, type, status);
		
		return new PageInfo<>(page);
	}

	@Override
	@Transactional
	public boolean createEntrustOrder(Long userId, Long marketId, BigDecimal price, BigDecimal num, Integer type, String payPassword) throws Exception {
		
		// 获取用户
		UserInfo user = userFeign.fetchUserInfo(userId);
		VaildateHelper.vaildateBooleanResult(user == null, RetResultCode.E20019, userId);
		
		// 获取市场
		Market market = marketMapper.queryByPrimaryKey(marketId);
		VaildateHelper.vaildateBooleanResult(market == null, RetResultCode.E30004);
		
		// 系统状态
		Integer sysStatus = Integer.parseInt(configFeign.fetchSystemConfigValue(SysParaKey.TRADE_CONFIG_SYSTEM_STATUS));
		VaildateHelper.vaildateBooleanResult(DDIC.Boolean.BOOL_FALSE_0.id == sysStatus, RetResultCode.E30002);
		
		// 验证交易时间
		Date currentTime = new Date();
        if (!TradeUtils.validateCanTrade(currentTime, market.getTradeWeek(), market.getTradeTime())) {
            throw new ClientException(RetResultCode.E11001, "不在交易时间范围内");
        }
		
		// 交易金额（BTCX）
        BigDecimal orderAmount = BigDecimalUtils.getRoundAmount(price.multiply(num), 8);
		// 交易手续费比例
        BigDecimal feeRate = null;
        // 交易费用 = 订单金额 * 交易手续费
        BigDecimal orderFee = null;
        // 冻结资金（包含手续费)
        BigDecimal freezeAmount = null;
		TradeType ttype = DDIC.TradeType.convertById(type);
		switch (ttype) {
		case BUY:
			// 价格校验，数量校验
            if (market.getBuyMin().doubleValue() > 0.0D && price.max(market.getBuyMin()) != price) {
                throw new ClientException(RetResultCode.E11001, "Invalid trade price, out of min buy price, the min price: " + market.getBuyMin() + ",current price: " + price);
            }
            if (market.getBuyMax().doubleValue() > 0.0D && price.min(market.getBuyMax()) != price) {
                throw new ClientException(RetResultCode.E11001, "Invalid trade price, out of max buy price, the max price: " + market.getBuyMax() + ",current price: " + price);
            }
            feeRate = market.getFeeBuy();
            orderFee = BigDecimalUtils.getRoundAmount(orderAmount.multiply(feeRate), 8);
            freezeAmount = orderAmount.add(orderFee);
			break;
		case SELL:
			if (market.getSellMin().doubleValue() > 0.0D && price.max(market.getSellMin()) != price) {
                throw new ClientException(RetResultCode.E11001, "Invalid trade price, out of min sell price, the min price: " + market.getSellMin() + ",current price: " + price);
            }
            if (market.getSellMax().doubleValue() > 0.0D && price.min(market.getSellMax()) != price) {
                throw new ClientException(RetResultCode.E11001, "Invalid trade price, out of max sell price, the max price: " + market.getSellMax() + ",current price: " + price);
            }
            feeRate = market.getFeeSell();
            freezeAmount = num;
            orderFee = BigDecimalUtils.getRoundAmount(orderAmount.multiply(feeRate), 8);
			break;
		default:
			// 抛异常
			VaildateHelper.vaildateBooleanResult(true, RetResultCode.E11001, "操作类型错误：" + type);
			break;
		}
		
        if (market.getTradeMax().doubleValue() > 0.0D && market.getTradeMin().doubleValue() > 0.0D
                && (orderAmount.max(market.getTradeMin()) != orderAmount
                || orderAmount.min(market.getTradeMax()) != orderAmount)) {
            throw new ClientException(RetResultCode.E11001, "Invalid trade amount, amount range [" + market.getTradeMin() + "," + market.getTradeMax() + "], current trade mount: " + orderAmount);
        }
        
        EntrustOrder order = new EntrustOrder();
        order.setOrderId(SequenceUtils.generateOrderSerialNum());
        order.setUserId(userId);
        order.setMarketId(marketId);
        order.setPrice(price);
        order.setNumber(num);
        order.setAmount(orderAmount); // 交易金额（BTCX）
        order.setFeeRate(feeRate);
        order.setFee(orderFee);
        order.setDealNumber(BigDecimalUtils.genInitValue());
        order.setLockNumber(freezeAmount);
        order.setType(type);
        order.setStatus(DDIC.EntrustOrderStatus.UNSETTLED.id);
        order.setCreateTime(currentTime);
        order.setModifiedTime(currentTime);
        
        // 入库
        boolean bool = entrustOrderMapper.insertSelective(order) > 0;
        if (bool) {
        	if (ttype == TradeType.BUY) {
        		this.orderCreateCaculate(userId, market.getBuyCoinId(), freezeAmount, order.getOrderId());
        	} else {
        		this.orderCreateCaculate(userId, market.getSellCoinId(), freezeAmount, order.getOrderId());
        	}
        	
        	this.setTask(order.getOrderId());
        	
        	return bool;
        }
        
		throw new ClientException(RetResultCode.HANDLER_FIELED);
	}
	
	public void orderCreateCaculate(long userId, long coinId, BigDecimal freezeAmount, long entrustOrderId) throws Exception {
		
		VaildateHelper.vaildateBooleanResult(freezeAmount == null || freezeAmount.doubleValue() <= 0.0, RetResultCode.E11001, "Invalid freeze amount: " + freezeAmount);
		
		CoinInfo coin = coinMapper.queryByPrimaryKey(coinId);
		VaildateHelper.vaildateBooleanResult(coin == null, RetResultCode.E30003, coinId);
		
		UserInfo user = userFeign.fetchUserInfo(userId);
		VaildateHelper.vaildateBooleanResult(user == null, RetResultCode.E20019, userId);
		
		CoinWallet coinWallet = coinWalletFeign.fetchCoinWalletInfo(userId, coinId);
		VaildateHelper.vaildateBooleanResult(coinWallet == null, RetResultCode.E40001);
		
		boolean isLocked = false;
		try {
			
			isLocked = this.redisService.lock(CacheConts.LOCK_COIN_WALLET, String.valueOf(coinWallet.getCoinWalletId()), true);
			if (!isLocked) {
				throw new CoinWalletSyncException(RetResultCode.E13004);
			}
			
			this.coinWalletFeign.walletFreezeAmount(userId, coinId, freezeAmount, OperatePaymentType.TRADE_DETAIL_REMARK_TYPE_ORDER_CREATE, entrustOrderId);
			
		} catch (Exception var19) {
			throw var19;
		} finally {
			if (isLocked) {
				// 释放锁
				this.redisService.unlock(CacheConts.LOCK_COIN_WALLET, String.valueOf(coinWallet.getCoinWalletId()));
			}

		}

	}

	@Override
	@Transactional
	public boolean cancelEntrustOrder(Long userId, Long orderId) throws Exception {

		// 获取委托单
		EntrustOrder order = entrustOrderMapper.queryByPrimaryKey(orderId);
		VaildateHelper.vaildateBooleanResult(order == null, RetResultCode.E50001, orderId);
		
		// 是否为此用户的订单
		VaildateHelper.vaildateBooleanResult(userId - order.getUserId() != 0, RetResultCode.E11001, "无法操作此订单");
		
		// 获取交易市场
		Market market = marketMapper.queryByPrimaryKey(order.getMarketId());
		VaildateHelper.vaildateBooleanResult(market == null, RetResultCode.E30004, order.getMarketId());
		
		Date currentTime = new Date();
		
		// 修改委托单状态
		EntrustOrder updateOrder = new EntrustOrder();
		updateOrder.setOrderId(orderId);
		updateOrder.setStatus(DDIC.EntrustOrderStatus.CANCELED.id);
		updateOrder.setLockNumber(BigDecimalUtils.genInitValue());
		updateOrder.setModifiedTime(currentTime);
		
		boolean bool = entrustOrderMapper.updateByPrimaryKeySelective(updateOrder) > 0;
		if (bool) {
			
			this.orderCancelCaculate(userId, DDIC.TradeType.BUY.id == order.getType() ? market.getBuyCoinId() : market.getSellCoinId(), order.getLockNumber(), order.getOrderId());
			
			return bool;
		}
		
		throw new ClientException(RetResultCode.HANDLER_FIELED);
	}

	public void orderCancelCaculate(long userId, long coinId, BigDecimal unfreezeAmount, long entrustOrderId) throws Exception {
		
		VaildateHelper.vaildateBooleanResult(unfreezeAmount == null || unfreezeAmount.doubleValue() <= 0.0, RetResultCode.E11001, "Invalid unfreeze amount: " + unfreezeAmount);
		
		CoinInfo coin = coinMapper.queryByPrimaryKey(coinId);
		VaildateHelper.vaildateBooleanResult(coin == null, RetResultCode.E30003, coinId);
		
		UserInfo user = userFeign.fetchUserInfo(userId);
		VaildateHelper.vaildateBooleanResult(user == null, RetResultCode.E20019, userId);
		
		CoinWallet coinWallet = coinWalletFeign.fetchCoinWalletInfo(userId, coinId);
		VaildateHelper.vaildateBooleanResult(coinWallet == null, RetResultCode.E40001);
		
		boolean isLocked = false;
		try {
			isLocked = this.redisService.lock(CacheConts.LOCK_COIN_WALLET, String.valueOf(coinWallet.getCoinWalletId()), true);
			if (!isLocked) {
				throw new CoinWalletSyncException(RetResultCode.E13004);
			}
			
			this.coinWalletFeign.walletUnfreezeAmount(userId, coinId, unfreezeAmount, OperatePaymentType.TRADE_DETAIL_REMARK_TYPE_ORDER_CANNCEL, entrustOrderId);
			
		} catch (Exception var19) {
			throw var19;
		} finally {
			if (isLocked) {
				// 释放锁
				this.redisService.unlock(CacheConts.LOCK_COIN_WALLET, String.valueOf(coinWallet.getCoinWalletId()));
			}
		}
	}
	
	@Override
	public void setTask(long orderId) throws Exception {
		// 将委托单放入缓存
    	this.redisService.rpush(CacheConts.CACHE_TASK_LIST, CacheConts.ENTRUST_ORDER, orderId);
	}

	@Override
	public Long getTask() throws Exception {
		
		boolean isLocked = false;
		try {
			isLocked = this.redisService.lock(CacheConts.CACHE_TASK_LIST, CacheConts.LOCK_TRADE_ENTRUST_ORDER_KEY, true);
			if (!isLocked) {
				throw new CoinWalletSyncException(RetResultCode.E13004);
			}
			
			String value = this.redisService.lpop(CacheConts.CACHE_TASK_LIST, CacheConts.ENTRUST_ORDER);
			if (StringUtils.isNotBlank(value)) {
				return Long.parseLong(value);
			}
			
		} catch (Exception var19) {
			throw var19;
		} finally {
			if (isLocked) {
				// 释放锁
				this.redisService.unlock(CacheConts.LOCK_COIN_WALLET, CacheConts.LOCK_TRADE_ENTRUST_ORDER_KEY);
			}
		}
		
		return 0L;
	}

	@Override
	public List<EntrustOrder> fetchRecentlyBuyEntrustList(Long marketId, int limit) throws Exception {

		List<EntrustOrder> rs = this.cacheDataService.getEntrustBuyOrder2Cache(marketId);
		
		return rs != null ? rs.subList(0, Math.min(rs.size(), limit)) : null;
	}
	
	@Override
	public List<EntrustOrder> fetchRecentlySellEntrustList(Long marketId, int limit) throws Exception {

		List<EntrustOrder> rs = this.cacheDataService.getEntrustSellOrder2Cache(marketId);
		
		return rs != null ? rs.subList(0, Math.min(rs.size(), limit)) : null;
	}

	@Override
	public List<DealOrder> fetchRecentlyDealList(Long marketId, int limit) throws Exception {

		List<DealOrder> rs = this.cacheDataService.getTurnoverOrder2Cache(marketId);
		
		return rs != null ? rs.subList(0, Math.min(rs.size(), limit)) : null;
	}

	@Override
	public void tradeOrderProcess() throws Exception {
		// 获取委托订单
		Long orderId = this.getTask();
		if (orderId == null || orderId <= 0L) {
			return;
		}
		
		Date currentTime = new Date(System.currentTimeMillis());
		try {
			LoggerHelper.printStartLog(mLog, "orderProcess", "orderId", new Object[]{orderId});
			boolean result = this.orderCaculate(orderId);
			if (result) {
				this.setTask(orderId);
			}
			LoggerHelper.printEndLog(mLog, "orderProcess", "orderId, processResult", new Object[]{orderId, "Process successful"});
		} catch (CoinWalletSyncException | CoinWalletOperationException e) {
			this.setTask(orderId);
		} catch (CoinWalletStatusFreezeException | CoinWalletFreezeNotEnoughException | CoinWalletBalanceNotEnoughException e) {
			LoggerHelper.printEndLog(mLog, "orderProcess", "orderId, processResult", new Object[]{orderId, e.getMessage() + ", orderProcessFailed " + orderId});
			EntrustOrder order = entrustOrderMapper.fetchEntrustOrderById(orderId);
            VaildateHelper.vaildateBooleanResult(order == null, RetResultCode.E50001, orderId);
            
            try {
                this.orderCancelCaculate(order, DDIC.EntrustOrderStatus.EXCEPTION.id);
                LoggerHelper.printEndLog(mLog, "orderProcess", "orderId, orderCancelResult", new Object[]{orderId, "cancel successful"});
            } catch (Exception e1) {
                e1.printStackTrace();
                LoggerHelper.printEndLog(mLog, "orderProcess", "orderId, orderCancelResult", new Object[]{orderId, "cancel failed: " + e1.getMessage()});
            }
		} catch (TradeOrderMatchFailedException | TradeOrderMatchRepeatException var17) {
			LoggerHelper.printEndLog(mLog, "orderProcess", "orderId, processResult", new Object[]{orderId, var17.getMessage()});
		} catch (Exception e) {
            e.printStackTrace();
            entrustOrderMapper.updateEntrustOrderStatus(orderId, 3, currentTime);
            LoggerHelper.printEndLog(mLog, "orderProcess", "orderId, processResult", new Object[]{orderId, e.getMessage() + ", orderProcessFailed " + orderId});
        } finally {
        	// 释放锁
			this.redisService.unlock(CacheConts.LOCK_COIN_WALLET, CacheConts.LOCK_TRADE_ENTRUST_ORDER_KEY);
        }
		
	}
	
	/**
     * 取消委托订单
     * @param order 待取消委托订单
     * @param orderStatus 订单状态
     * @throws Exception
     */
	@Transactional
    private void orderCancelCaculate(EntrustOrder order, int orderStatus) throws Exception {
        
    	VaildateHelper.vaildateBooleanResult(order == null, RetResultCode.E50001);
    	
    	if (DDIC.EntrustOrderStatus.UNSETTLED.id == order.getStatus()) {
    		Market market = marketMapper.fetchMarketInfoById(order.getMarketId());
    		VaildateHelper.vaildateBooleanResult(market == null, RetResultCode.E30004);
    		
    		Date currentTime = new Date(System.currentTimeMillis());
    		int count = entrustOrderMapper.updateEntrustOrderDealAndStatus(order.getOrderId(), orderStatus, BigDecimalUtils.genInitValue(), BigDecimalUtils.genInitValue(), currentTime);
    		if (1 == count) {
    			BigDecimal unfreezeAmount = order.getLockNumber();
    			this.orderCancelCaculate(order.getUserId(), DDIC.TradeType.BUY.id == order.getType() ? market.getBuyCoinId() : market.getSellCoinId(), unfreezeAmount, order.getOrderId());
    		}
    	}
    	
    }

	@Transactional
	private boolean orderCaculate(long orderId) throws Exception {
		// 获取订单信息
		EntrustOrder order = entrustOrderMapper.queryByPrimaryKey(orderId);
		VaildateHelper.vaildateBooleanResult(order == null, RetResultCode.E50001, orderId);
		
		if (DDIC.EntrustOrderStatus.UNSETTLED.id != order.getStatus()) {
			return false;
		}
		
		/* 需要成交的数量 */
        BigDecimal needDeal = order.getNumber().subtract(order.getDealNumber());
        /* 未成交或未全部成交 */
        if (needDeal != null && needDeal.doubleValue() > 0.0D) {
        	 /* 查询匹配的委托单类型 */
        	int type = DDIC.TradeType.BUY.id == order.getType() ? DDIC.TradeType.SELL.id : DDIC.TradeType.BUY.id;
        	/* 查询匹配的委托单 */
        	EntrustOrder matchs = entrustOrderMapper.fetchMatchOrder(order.getMarketId(), type, order.getPrice());
        	if (matchs != null && order.getType() != matchs.getType()) {
        		// 撮合买单
        		EntrustOrder buyOrder = order.getType() == DDIC.TradeType.BUY.id ? order : matchs;
                // 撮合卖单
        		EntrustOrder sellOrder = order.getType() == DDIC.TradeType.BUY.id ? matchs : order;
        		// 成交单
        		DealOrder turnoverOrder = dealOrderMapper.fetchByOrderIdAndStatus(order.getMarketId(), buyOrder.getOrderId(), sellOrder.getOrderId(), DDIC.DealOrderStatus.UNSETTLED.id);
        		if (turnoverOrder == null) {
        			// 交易市场
        			Market market = marketMapper.queryByPrimaryKey(order.getMarketId());
        			VaildateHelper.vaildateBooleanResult(market == null, RetResultCode.E30004, order.getMarketId());
        			
        			// 获取系统当前时间
                    Date currentTime = new Date(System.currentTimeMillis());
                    // 可以买的数量 = 委托数量 - 已成交数量
                    BigDecimal canBuyNum = buyOrder.getNumber().subtract(buyOrder.getDealNumber());
                    // 可卖数量 = 委托数量 - 已成交数量
                    BigDecimal canSellNum = sellOrder.getNumber().subtract(sellOrder.getDealNumber());
                    // 本次撮合成交数量（可卖量和可买量取小）
                    BigDecimal num = canBuyNum.min(canSellNum) == canBuyNum ? canBuyNum : canSellNum;
                    // 成交价 = 买单取卖价，卖单取买价
                    BigDecimal price = order.getType() == DDIC.TradeType.BUY.id ? sellOrder.getPrice() : buyOrder.getPrice();
                    // 成交金额 = 成交量 * 成交价
                    BigDecimal turnoverAmount = BigDecimalUtils.getRoundAmount(num.multiply(price), Global.RETAIN_DECIMAL_LEN);
                    // 买入手续费 = 成交金额 * 交易市场配置的买入手续费率
                    BigDecimal buyFee = BigDecimalUtils.getRoundAmount(turnoverAmount.multiply(market.getFeeBuy()), Global.RETAIN_DECIMAL_LEN);
                    // 卖出手续费 = 成交金额 * 交易市场配置的卖出手续费率
                    BigDecimal sellerFee = BigDecimalUtils.getRoundAmount(turnoverAmount.multiply(market.getFeeSell()), Global.RETAIN_DECIMAL_LEN);
                    // 新的买入冻结金额  = 买入价 * （委托买入量 - 已成交量 - 本次成交量）
                    BigDecimal newBuyFreezeAmount = BigDecimalUtils.getRoundAmount(buyOrder.getPrice().multiply(buyOrder.getNumber().subtract(buyOrder.getDealNumber()).subtract(num)), Global.RETAIN_DECIMAL_LEN);
                    // 新的买入冻结金额 = 新的买入冻结金额 + (newBuyFreezeAmount * 买入手续费比例)
                    newBuyFreezeAmount = newBuyFreezeAmount.add(BigDecimalUtils.getRoundAmount(newBuyFreezeAmount.multiply(buyOrder.getFeeRate()), Global.RETAIN_DECIMAL_LEN));
                    // 新的卖出冻结资金 = 委托卖出量 - 已成交量 - 本次成交量
                    BigDecimal newSellFreezeNum = sellOrder.getNumber().subtract(sellOrder.getDealNumber()).subtract(num);

                    turnoverOrder = new DealOrder();
                    turnoverOrder.setDealId(SequenceUtils.generateOrderSerialNum());
                    turnoverOrder.setMarketId(market.getMarketId()); // 交易市场ID
                    turnoverOrder.setBuyOrderId(buyOrder.getOrderId()); // 委托买单ID
                    turnoverOrder.setSellOrderId(sellOrder.getOrderId()); // 委托卖单ID
                    turnoverOrder.setDealPrice(price); // 成交价格
                    turnoverOrder.setDealNumber(num); // 成交数量
                    turnoverOrder.setDealAmount(turnoverAmount); // 成交金额
                    turnoverOrder.setDealBuyFeeRate(market.getFeeBuy()); // 买入手续费比例
                    turnoverOrder.setDealBuyFee(buyFee); // 买入手续费
                    turnoverOrder.setDealSellFeeRate(market.getFeeSell()); // 成交卖出手续费率
                    turnoverOrder.setDealSellFee(sellerFee); // 卖出手续费
                    turnoverOrder.setStatus(DDIC.DealOrderStatus.TRADED.id); // 成交状态
                    turnoverOrder.setCreateTime(new Date()); // 交易时间
                    turnoverOrder.setModifiedTime(new Date()); // 修改时间
                    
                    // 创建成交单记录
                    dealOrderMapper.insertSelective(turnoverOrder);
                    
                    if (num.max(canSellNum) == num) {
                        // 刚好全部成交，更新委托卖单状态
                    	entrustOrderMapper.updateEntrustOrderDealAndStatus(sellOrder.getOrderId(), DDIC.EntrustOrderStatus.TRADED.id, num, BigDecimalUtils.genInitValue(), currentTime);
                    } else {
                        // 买单全部成交，卖单部分成交
                    	entrustOrderMapper.updateEntrustOrderDeal(sellOrder.getOrderId(), num, newSellFreezeNum, currentTime);
                    }
                    
                    if (num.max(canBuyNum) == num) {
                        // 刚好全部成交
                    	entrustOrderMapper.updateEntrustOrderDealAndStatus(buyOrder.getOrderId(), DDIC.EntrustOrderStatus.TRADED.id, num, BigDecimalUtils.genInitValue(), currentTime);
                    } else {
                        // 卖单全部成交，买单部分成交
                    	entrustOrderMapper.updateEntrustOrderDeal(buyOrder.getOrderId(), num, newBuyFreezeAmount, currentTime);
                    }
                    
                    // 委托订单撮合成功后，修改买卖房账户余额信息
                    this.orderProcessCaculate(
                            buyOrder.getUserId(),            // 买入方用户ID
                            market.getBuyCoinId(),           // 买入方币种ID
                            sellOrder.getUserId(),           // 委托卖出用户ID
                            market.getSellCoinId(),          // 委托卖出币种
                            buyOrder.getLockNumber(),                           // 买单冻结资金
                            sellOrder.getLockNumber(),                          // 卖单冻结资金
                            newBuyFreezeAmount,                             // 买单最新冻结资金
                            newSellFreezeNum,                               // 卖单最新冻结数量
                            turnoverOrder.getDealAmount(),              // 成交金额
                            turnoverOrder.getDealNumber(),                 // 成交数量
                            turnoverOrder.getDealBuyFee(),              // 买入手续费
                            turnoverOrder.getDealSellFee(),             // 卖出手续费
                            turnoverOrder.getDealId());                         // 撮合成交单号

                    return num.max(needDeal) != num;
        		} else {
        			throw new TradeOrderMatchRepeatException(RetResultCode.E50002, "Order match repeat.");
        		}
        	} else {
        		throw new TradeOrderMatchFailedException(RetResultCode.E50003, "Order match failed.");
        	}
        }
        
        return false;
	}
	
	/**
	 * 委托订单撮合成功后，修改账户余额数据
	 * @param buyUserId 买单用户
	 * @param buyCoinId 买单币种
	 * @param sellUserId 卖单用户
	 * @param sellCoinId 买单币种
	 * @param buyUnfreezeAmount 买单冻结资金
	 * @param sellUnfeezeNum 卖单冻结数量
	 * @param buyFreezeAmount 买单最新冻结资金
	 * @param sellFreezeNum 卖单最新冻结数量
	 * @param buyTurnoverAmount 买单成交金额
	 * @param sellTurnoverNum 卖单成交数量
	 * @param buyTurnoverFee 买单成交手续费
	 * @param sellTurnoverFee 卖单成交手续费
	 * @param turnoverOrderId 撮合订单ID
	 * @throws Exception
	 */
	@Transactional
	private void orderProcessCaculate(
			long buyUserId,
			long buyCoinId,
			long sellUserId,
			long sellCoinId,
			BigDecimal buyUnfreezeAmount,
			BigDecimal sellUnfeezeNum,
			BigDecimal buyFreezeAmount,
			BigDecimal sellFreezeNum,
			BigDecimal buyTurnoverAmount,
			BigDecimal sellTurnoverNum,
			BigDecimal buyTurnoverFee,
			BigDecimal sellTurnoverFee,
			long turnoverOrderId) throws Exception {

		UserInfo buyUser = userFeign.fetchUserInfo(buyUserId);
		VaildateHelper.vaildateBooleanResult(buyUser == null, RetResultCode.E20019, buyUserId);
		
		CoinInfo buyCoin = coinMapper.queryByPrimaryKey(buyCoinId);
		VaildateHelper.vaildateBooleanResult(buyCoin == null, RetResultCode.E30003, buyCoinId);
		
		UserInfo sellUser = userFeign.fetchUserInfo(sellUserId);
		VaildateHelper.vaildateBooleanResult(sellUser == null, RetResultCode.E20019, sellUserId);
		
		CoinInfo sellCoin = coinMapper.queryByPrimaryKey(sellCoinId);
		VaildateHelper.vaildateBooleanResult(sellCoin == null, RetResultCode.E30003, sellCoinId);
		
		// 买单支付币种账户
		CoinWallet buyWallet = coinWalletFeign.fetchCoinWalletInfo(buyUserId, buyCoinId);
		// 买单用户卖出币种账户
		CoinWallet sellWallet = coinWalletFeign.fetchCoinWalletInfo(sellUserId, sellCoinId);
		// 系统买入币种账户
		// 获取平台钱包账号
		Long platformUserId = userFeign.fetchAdminUserId();
		CoinWallet buyAdminWallet = coinWalletFeign.fetchCoinWalletInfo(platformUserId, buyCoinId);
		VaildateHelper.vaildateBooleanResult(buyAdminWallet == null, RetResultCode.E40001);
		
		boolean isBuyUserLocked = false;
		boolean isSellUserLocked = false;
		boolean isAdminUserLocked = false;
		try {
			isBuyUserLocked = this.redisService.lock(CacheConts.LOCK_COIN_WALLET, String.valueOf(buyWallet.getCoinWalletId()), false);
			isSellUserLocked = this.redisService.lock(CacheConts.LOCK_COIN_WALLET, String.valueOf(sellWallet.getCoinWalletId()), false);
			if (!isBuyUserLocked || !isSellUserLocked) {
				throw new CoinWalletSyncException(RetResultCode.E13004);
			}
			isAdminUserLocked = this.redisService.lock(CacheConts.LOCK_COIN_WALLET, String.valueOf(buyAdminWallet.getCoinWalletId()), false);
			if (!isAdminUserLocked) {
				throw new CoinWalletSyncException(RetResultCode.E13004);
			}

			// 解锁买单支付币种账户，并创建账户详情记录
			this.coinWalletFeign.walletUnfreezeAmount(buyUser.getUserId(), buyCoin.getCoinId(), // 买单钱包
					buyUnfreezeAmount, // 买单冻结资金
					OperatePaymentType.TRADE_DETAIL_REMARK_TYPE_ORDER_TURNOVER, 
					turnoverOrderId); // 撮合订单ID
			
			// 解锁买单账户
			this.coinWalletFeign.walletUnfreezeAmount(sellUser.getUserId(), sellCoin.getCoinId(), // 卖单用户
					sellUnfeezeNum, // 卖单冻结资金
					OperatePaymentType.TRADE_DETAIL_REMARK_TYPE_ORDER_TURNOVER, 
					turnoverOrderId); // 撮合订单ID

			// 更新买单用户账户余额
			this.coinWalletFeign.transferAmount(buyWallet.getCoinWalletId(), 
					sellWallet.getCoinWalletId(), 
					buyCoinId, 
					buyTurnoverAmount, 
					OperatePaymentType.TRADE_DETAIL_REMARK_TYPE_ORDER_TURNOVER, 
					turnoverOrderId);

			// 更新卖单用户账户余额
			this.coinWalletFeign.transferAmount(sellWallet.getCoinWalletId(), 
					buyWallet.getCoinWalletId(), 
					sellCoinId, 
					sellTurnoverNum, 
					OperatePaymentType.TRADE_DETAIL_REMARK_TYPE_ORDER_TURNOVER, 
					turnoverOrderId);
			
			// 收取买单手续费
			this.coinWalletFeign.transferAmount(buyWallet.getCoinWalletId(), 
					buyAdminWallet.getCoinWalletId(), // 主账户用户
					buyCoinId, // 买单支付币种ID
					buyTurnoverFee, // 买单手续费
					OperatePaymentType.TRADE_DETAIL_REMARK_TYPE_ORDER_TURNOVER_POUNDAGE, 
					turnoverOrderId);
			
			// 收取卖单手续费
			this.coinWalletFeign.transferAmount(sellWallet.getCoinWalletId(), 
					buyAdminWallet.getCoinWalletId(), // 主账户用户
					sellCoinId, // 卖单币种ID
					sellTurnoverFee, // 卖单手续费
					OperatePaymentType.TRADE_DETAIL_REMARK_TYPE_ORDER_TURNOVER_POUNDAGE, 
					turnoverOrderId);

			// 最新冻结资金
			if (buyFreezeAmount != null && buyFreezeAmount.doubleValue() > 0.0D) {
				// 冻结资金
				this.coinWalletFeign.walletFreezeAmount(buyUser.getUserId(), buyCoin.getCoinId(), // 买单用户
						buyFreezeAmount, // 买单冻结资金
						OperatePaymentType.TRADE_DETAIL_REMARK_TYPE_ORDER_TURNOVER, 
						turnoverOrderId); // 关联订单号
			}
			// 最新冻结币数量
			if (sellFreezeNum != null && sellFreezeNum.doubleValue() > 0.0D) {
				this.coinWalletFeign.walletFreezeAmount(sellUser.getUserId(), sellCoin.getCoinId(), // 卖单用户
						sellFreezeNum, // 卖单冻结资金
						OperatePaymentType.TRADE_DETAIL_REMARK_TYPE_ORDER_TURNOVER, 
						turnoverOrderId); // 关联订单号
			}
			
		} catch (Exception e) {
			throw e;
		} finally {
			// 释放锁
			if (isBuyUserLocked) {
				this.redisService.unlock(CacheConts.LOCK_COIN_WALLET, String.valueOf(buyWallet.getCoinWalletId()));
			}
			if (isSellUserLocked) {
				this.redisService.unlock(CacheConts.LOCK_COIN_WALLET, String.valueOf(sellWallet.getCoinWalletId()));
			}
			if (isAdminUserLocked) {
				this.redisService.unlock(CacheConts.LOCK_COIN_WALLET, String.valueOf(buyAdminWallet.getCoinWalletId()));
			}
		}
	}
	
}
